#include "dtls_transport.h"
#include <rtc_base/logging.h>
#include <api/crypto/crypto_options.h>
namespace xrtc
{
        const size_t k_dtls_record_header_len = 13;
        const size_t k_max_dtls_packet_len = 2048; // packet size
        const size_t k_max_pending_packets = 2;    // pakcet num
        const size_t k_min_rtp_packet_len = 12;    // rtp packet min_size
        bool IsDtlsPacket(const char *buf, size_t len)
        {
                const uint8_t *u = reinterpret_cast<const uint8_t *>(buf);
                return len >= k_dtls_record_header_len && (u[0] > 19 && u[0] < 64);
        }
        bool IsDtlsClientHelloPacket(const char *buf, size_t len)
        {
                if (!IsDtlsPacket(buf, len))
                {
                        return false;
                }
                const uint8_t *u = reinterpret_cast<const uint8_t *>(buf);
                return len > 17 && (u[0] == 22 && u[13] == 1);
        }
        // StreamInterfaceChannel
        StreamInterfaceChannel::StreamInterfaceChannel(IceTransportChannel *ice_channel) : ice_channel_(ice_channel),
                                                                                           _packets(k_max_pending_packets, k_max_dtls_packet_len)
        {
        }
        bool StreamInterfaceChannel::OnReceivedPacket(const char *data, size_t size)
        {
                if (_packets.size() > 0)
                {
                        RTC_LOG(LS_INFO) << ": Packet already in buffer queue";
                }
                if (!_packets.WriteBack(data, size, NULL))
                {
                        RTC_LOG(LS_WARNING) << ": Failed to write packet to queue";
                }

                SignalEvent(this, rtc::SE_READ, 0); // SSLStreamAdapter => catpure signal
                return true;
        }

        rtc::StreamState StreamInterfaceChannel::GetState() const
        {
                return state_;
        }
        rtc::StreamResult StreamInterfaceChannel::Read(void *buf, size_t buffer_len, size_t *read, int * /*error*/)
        {
                if (state_ == rtc::SS_CLOSED) // stream close
                {
                        return rtc::StreamResult::SR_EOS;
                }
                if (state_ == rtc::SS_OPENING)
                {
                        return rtc::StreamResult::SR_BLOCK;
                }
                if (!_packets.ReadFront(buf, buffer_len, read))
                {
                        return rtc::StreamResult::SR_BLOCK;
                }

                return rtc::StreamResult::SR_SUCCESS;
        }
        rtc::StreamResult StreamInterfaceChannel::Write(const void *buf, size_t buffer_len, size_t *written, int * /*error*/)
        {
                ice_channel_->SendPacket((const char *)buf, buffer_len);
                if (written)
                {
                        *written = buffer_len;
                }
                return rtc::StreamResult::SR_SUCCESS;
        }
        void StreamInterfaceChannel::Close()
        {
                state_ = rtc::SS_CLOSED;
                _packets.Clear();
                // return;
        }

        // DtlsTransport

        DtlsTransport::DtlsTransport(IceTransportChannel *ice_channel) : ice_channel_(ice_channel)
        {

                ice_channel_->SignalReadPacket.connect(this, &DtlsTransport::OnReadPacket);
                ice_channel_->SignalWritableState.connect(this, &DtlsTransport::OnWritableState);
                ice_channel_->SignalReceivingState.connect(this, &DtlsTransport::OnReceivingState);
                webrtc::CryptoOptions crypto_options;

                srtp_ciphers_ = crypto_options.GetSupportedDtlsSrtpCryptoSuites();
        }

        DtlsTransport::~DtlsTransport()
        {
        }
        void DtlsTransport::OnReceivingState(IceTransportChannel *channel)
        {

                SetReceiving(channel->Receiving());
        }
        void DtlsTransport::SetReceiving(bool receiving)
        {

                if (receiving_ == receiving)
                {
                        return;
                }

                RTC_LOG(LS_INFO) << ToString() << ": Change  receiving  " << receiving_ << "  to  " << receiving;
                receiving_ = receiving;
                SignalReceivingState(this);
        }
        void DtlsTransport::OnWritableState(IceTransportChannel *channel)
        {
                //    RTC_LOG(LS_INFO) << "===:IceTransportChannel writable channge to " << channel->Writable();
                if (!dtls_active_)
                {
                        SetWritableState(channel->Writable());
                        return;
                }
                switch (dtls_state_)
                {
                case DtlsTransportState::kNew:
                        MaybeStartDtls();
                        break;
                case DtlsTransportState::kConnected:
                        SetWritableState(channel->Writable());
                        break;
                default:
                        break;
                }
        }
        void DtlsTransport::OnReadPacket(IceTransportChannel * /*channel*/, const char *buf, size_t len, int64_t ts)
        {
                // RTC_LOG(LS_INFO)<<"====DTLS packet Len : "<<len;
                switch (dtls_state_)
                {
                case DtlsTransportState::kNew:
                {
                        if (dtls_)
                        {
                                RTC_LOG(LS_INFO) << ToString() << ": Received packet before DTLS started.";
                        }
                        else
                        {
                                RTC_LOG(LS_WARNING) << ToString() << ": Received packet before we know if"
                                                    << " we are doing DTLS or not";
                        }
                        if (IsDtlsClientHelloPacket(buf, len))
                        {

                                RTC_LOG(LS_INFO) << ToString() << ": Caching DTLS ClientHello packet until  DTLS started";
                                cached_client_hello_.SetData(buf, len);
                                if (!dtls_ && local_certificate_)
                                {
                                        SetupDtls();
                                }
                        }
                        else
                        {
                                RTC_LOG(LS_WARNING) << ToString() << ": Not a Dtls ClientHello packet,droping";
                        }
                }
                break;
                case DtlsTransportState::kConnecting:
                case DtlsTransportState::kConnected:
                {
                        if (IsDtlsPacket(buf, len)) // 还是 dtls包
                        {
                                if (!HandleDtlsPacket(buf, len))
                                {

                                        RTC_LOG(LS_WARNING) << ToString() << ": Handle DTLS packet failed";
                                        return;
                                }
                        }
                        else
                        { // rtp/rtcp/other packet

                                if (dtls_state_ != DtlsTransportState::kConnected)
                                {
                                        RTC_LOG(LS_WARNING) << ToString() << ": Received non DTLS packet"
                                                            << "before DTLS complete";
                                        return;
                                }

                                if (!IsRtpPacket(buf, len))
                                {
                                        RTC_LOG(LS_WARNING) << ToString() << ": Received unexpected non DTLS packet"
                                                            << " DTLS packet";
                                        return;
                                }
                                // RTC_LOG(LS_INFO) << ToString() << ":====rtp received: " << len;
                                SignalReadPacket(this, buf, len, ts);
                        }
                }
                break;
                default:
                        break;
                }
        }
        std::string DtlsTransport::ToString()
        {

                std::stringstream ss;
                absl::string_view RECEIVING[2] = {"-", "R"};
                absl::string_view WRITABLE[2] = {"-", "W"};
                ss << "DtlsTransport[" << TransportName() << "|"
                   << RECEIVING[receiving_] << "|"
                   << WRITABLE[writable_] << "]";
                return ss.str();
        }
        bool DtlsTransport::SetupDtls()
        {
                auto downward = std::make_unique<StreamInterfaceChannel>(ice_channel_);
                StreamInterfaceChannel *downward_ptr = downward.get();
                dtls_ = rtc::SSLStreamAdapter::Create(std::move(downward));
                if (!dtls_)
                {
                        RTC_LOG(LS_WARNING) << ToString() << ": Failed to create SSLStreamAdapter";
                        return false;
                }
                downward_ = downward_ptr;
                dtls_->SetIdentity(local_certificate_->identity()->Clone());
                dtls_->SetMode(rtc::SSL_MODE_DTLS);
                dtls_->SetMaxProtocolVersion(rtc::SSL_PROTOCOL_DTLS_12);
                dtls_->SetServerRole(rtc::SSL_SERVER);
                dtls_->SignalEvent.connect(this, &DtlsTransport::OnDtlsEvent);
                dtls_->SignalSSLHandshakeError.connect(this, &DtlsTransport::OnDtlsHandshakeError);

                if (remote_fingerprint_value_.size() &&
                    !dtls_->SetPeerCertificateDigest(remote_fingerprint_alg_,
                                                     remote_fingerprint_value_.data(),
                                                     remote_fingerprint_value_.size()))
                {
                        RTC_LOG(LS_WARNING) << ToString() << ": Failed to set remote fingerpint";
                        return false;
                }
                if (!srtp_ciphers_.empty())
                {
                        if (!dtls_->SetDtlsSrtpCryptoSuites(srtp_ciphers_))
                        {
                                RTC_LOG(LS_WARNING) << ToString() << ": Failed to set DTLS-SRTP crypto suites";
                                return false;
                        }
                }
                else
                {
                        RTC_LOG(LS_WARNING) << ToString() << ": Not using DTLS-SRTP";
                }
                RTC_LOG(LS_INFO) << ToString() << ": Setup dtls complete";
                MaybeStartDtls();
                return true;
        }
        void DtlsTransport::OnDtlsHandshakeError(rtc::SSLHandshakeError err)
        {
                RTC_LOG(LS_WARNING) << ToString() << ":DTLS handshake error " << (int)err;
        }
        void DtlsTransport::OnDtlsEvent(rtc::StreamInterface * /*dtls*/, int sig, int error)
        {
                if (sig & rtc::SE_OPEN)
                {
                        RTC_LOG(LS_INFO) << ToString() << ": DTLS handshake complete.";
                        SetWritableState(true);
                        SetDtlsState(DtlsTransportState::kConnected);
                }
                if (sig & rtc::SE_READ)
                {
                        char buf[k_max_dtls_packet_len];
                        size_t read;
                        int read_error;
                        rtc::StreamResult ret;
                        // 因为一个数据包可能包含多个DTLS record，需要循环读取
                        do
                        {

                                ret = dtls_->Read(buf, sizeof(buf), &read, &read_error);
                                if (ret == rtc::SR_SUCCESS)
                                {
                                }
                                else if (ret == rtc::SR_EOS)
                                {
                                        RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed by remote .";
                                        SetWritableState(false);
                                        SetDtlsState(DtlsTransportState::kClosed);
                                        SignalClosed(this);
                                }
                                else if (ret == rtc::SR_ERROR)
                                {
                                        RTC_LOG(LS_WARNING) << ToString() << ": Closed DTLS transport by remote "
                                                            << "with errir,code=" << read_error;
                                        SetWritableState(false);
                                        SetDtlsState(DtlsTransportState::kFailed);
                                        SignalClosed(this);
                                }
                        } while (ret == rtc::SR_SUCCESS);
                }
                if (sig & rtc::SE_CLOSE)
                {
                        if (!error)
                        {
                                RTC_LOG(LS_INFO) << ToString() << ": DDTLS transport closed";
                                SetWritableState(false);
                                SetDtlsState(DtlsTransportState::kClosed);
                        }
                        else
                        {

                                RTC_LOG(LS_INFO) << ToString() << ": DDTLS transport closed  with error,"
                                                 << " code=" << error;
                                SetWritableState(false);
                                SetDtlsState(DtlsTransportState::kFailed);
                        }
                }
        }
        bool DtlsTransport::IsRtpPacket(const char *buf, size_t len)
        {
                const uint8_t *u = reinterpret_cast<const uint8_t *>(buf);
                return len >= k_min_rtp_packet_len && ((u[0] & 0xc0) == 0x80);
        }
        void DtlsTransport::MaybeStartDtls()
        {

                if (dtls_ && ice_channel_->Writable())
                {
                        if (dtls_->StartSSL()) // success =0;
                        {
                                RTC_LOG(LS_WARNING) << ToString() << ": Failed to StartSSL.";
                                SetDtlsState(DtlsTransportState::kFailed);
                                return;
                        }
                        RTC_LOG(LS_INFO) << ToString() << ":  Start STLS";
                        SetDtlsState(DtlsTransportState::kConnecting);

                        if (cached_client_hello_.size())
                        {
                                if (!HandleDtlsPacket(cached_client_hello_.data<char>(), cached_client_hello_.size()))
                                {
                                        RTC_LOG(LS_WARNING) << ToString() << ": Handling dtls packet failed";
                                        SetDtlsState(DtlsTransportState::kFailed);
                                }
                                cached_client_hello_.Clear();
                        }
                }
        }
        bool DtlsTransport::SetLocalCertificate(rtc::RTCCertificate *cert)
        {

                if (dtls_active_)
                {
                        if (cert == local_certificate_)
                        {
                                RTC_LOG(LS_INFO) << ToString() << ":Ingnoring identical DTLS cert";
                                return true;
                        }
                        else
                        {
                                RTC_LOG(LS_WARNING) << ToString() << ": Cannot change cert in this state";
                                return false;
                        }
                }
                if (cert)
                {
                        local_certificate_ = cert;
                        dtls_active_ = true;
                }
                return true;
        }

        bool DtlsTransport::SetRemoteFingerprint(const std::string &digest_alg, const uint8_t *digest, size_t digest_len)
        {
                rtc::Buffer remote_fingerprint_value(digest, digest_len);
                if (dtls_active_ && remote_fingerprint_value_ == remote_fingerprint_value && !digest_alg.empty())
                {
                        RTC_LOG(LS_INFO) << ToString() << ":Ignoring identical remote fingerprint";
                        return true;
                }
                if (digest_alg.empty())
                {

                        RTC_LOG(LS_WARNING) << ToString() << ": Other sides not support DTLS";
                        dtls_active_ = false;
                        return false;
                }
                if (!dtls_active_)
                {
                        RTC_LOG(LS_WARNING) << ToString() << ": Cannot set remote fingerprint in this state";
                        return false;
                }
                bool fingerprint_change = remote_fingerprint_value_.size() > 0u;
                remote_fingerprint_value_ = std::move(remote_fingerprint_value);
                remote_fingerprint_alg_ = digest_alg;

                // client hello packet 先到，answer sdp 后到
                if (dtls_ && !fingerprint_change)
                {
                        rtc::SSLPeerCertificateDigestError err;
                        if (dtls_ && !dtls_->SetPeerCertificateDigest(digest_alg, (const unsigned char *)digest, digest_len, &err))
                        {
                                RTC_LOG(LS_WARNING) << ToString() << ": Failed to set peer certifaicate digest";
                                SetDtlsState(DtlsTransportState::kFailed);
                                return err == rtc::SSLPeerCertificateDigestError::VERIFICATION_FAILED;
                        }
                        return true;
                }
                // canswer sdp先到， Client hello packet后到
                if (dtls_ && fingerprint_change)
                {

                        dtls_.reset(nullptr);
                        SetDtlsState(DtlsTransportState::kNew);
                        SetWritableState(false);
                }
                if (!SetupDtls())
                {
                        RTC_LOG(LS_WARNING) << ToString() << ": Failed to setup DTLS";
                        SetDtlsState(DtlsTransportState::kFailed);
                        return false;
                }

                return true;
        }

        void DtlsTransport::SetDtlsState(DtlsTransportState state)
        {

                if (dtls_state_ == state)
                {
                        return;
                }
                RTC_LOG(LS_INFO) << ToString() << ": Change  dtls state  " << dtls_state_ << " from " << state;
                dtls_state_ = state;
                SignalDtlsState(this, state);
        }
        void DtlsTransport::SetWritableState(bool writable)
        {
                if (writable_ == writable)
                {
                        return;
                }

                RTC_LOG(LS_INFO) << ToString() << ": Set  dtls writable  " << writable_ << "  to  " << writable;
                writable_ = writable;
                SignalWritableState(this);
        }
        bool DtlsTransport::HandleDtlsPacket(const char *data, size_t size) // data check func
        {

                const uint8_t *tmp_data = reinterpret_cast<const uint8_t *>(data);
                size_t tmp_size = size;
                while (tmp_size > 0)
                {

                        if (tmp_size < k_dtls_record_header_len)
                        {
                                return false;
                        }
                        size_t record_len = (tmp_data[11] << 8 | tmp_data[12]);
                        if (record_len + k_dtls_record_header_len > tmp_size)
                        {
                                return false;
                        }
                        tmp_data += k_dtls_record_header_len + record_len;
                        tmp_size -= k_dtls_record_header_len + record_len;
                }
                return downward_->OnReceivedPacket(data, size);
        }

        bool DtlsTransport::GetSrtpCryptoSuite(int *selected_crypto_suite)
        {
                if (dtls_state_ != DtlsTransportState::kConnected)
                {
                        return false;
                }
                return dtls_->GetDtlsSrtpCryptoSuite(selected_crypto_suite);
        }

        bool DtlsTransport::ExportKeyingMeterial(const std::string &label, const uint8_t *context, size_t context_len,
                                                 bool use_context, uint8_t *result, size_t result_len)
        {
                return dtls_.get() ? dtls_->ExportKeyingMaterial(label, context, context_len,
                                                                 use_context, result, result_len)
                                   : false;
        }
        int DtlsTransport::SendPacket(const char *data, size_t len)
        {

                if (ice_channel_)
                {
                        return ice_channel_->SendPacket(data, len);
                }
                return -1;
        }

} // namespace xrtc;
